/********************基于管道的进程池,父进程去往管道里写，不同子进程去读*********************/ 
#include <iostream>
#include <cstdlib>
#include <string>
#include <cassert>
#include <unistd.h>
#include <vector>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#define PROCSS_NUM 5                                                                           // 子进程个数
#define MAKE_RAND() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x5462ab22 ^ rand() % 123) // 生成随机数种子
class subEndPoint                                                                              // 管道的一端
{
public:
    subEndPoint(pid_t subId, int writeFd)
        : _subId(subId), _writeFd(writeFd)
    {
        char nameBuffer[1024];
        snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d)-fd(%d)]", num++, _subId, _writeFd);
        _name = nameBuffer;
    }

public:
    static int num;
    std::string _name; // 进程信息
    pid_t _subId;      // 子进程id
    int _writeFd;      // 管道的写端
};
int subEndPoint::num = 0;

/*****************子进程要完成的任务*******************/
// 函数指针 类型   也可以选择包装器实现
typedef void (*func_t)();
void downLoadTask()
{
    std::cout << getpid() << ": 下载任务\n"
              << std::endl;
    sleep(1);
}

void ioTask()
{
    std::cout << getpid() << ": IO任务\n"
              << std::endl;
    sleep(1);
}

void flushTask()
{
    std::cout << getpid() << ": 刷新任务\n"
              << std::endl;
    sleep(1);
}
void loadTaskFunc(std::vector<func_t> *FuncMap)
{
    assert(FuncMap);
    FuncMap->push_back(downLoadTask);
    FuncMap->push_back(ioTask);
    FuncMap->push_back(flushTask);
}
/********************************************************/
// 任务分配
void sendTask(const subEndPoint &process, int taskindex)
{
    std::cout << "send task index:" << taskindex << "send to ->" << process._name << std::endl;
    //&taskindex拿到起始地址，二进制写入【sizeof taskindex】个字节
    int n = write(process._writeFd, &taskindex, sizeof(taskindex));
    assert(n == sizeof(int)); // 保证生效
    (void)n;                  // debug版本assert生效，但是release版本不生效，没人使用的话就给void
}
/********************子进程读数据***********************/
int recvTask(int readFd)
{
    int code = 0;
    ssize_t s = read(readFd, &code, sizeof(code));
    // assert(s==sizeof(int));
    if (s == 4)
        return code;
    else if (s <= 0)
        return -1;
    else
        return 0;
}
/******************************************/
// 创建子进程并建立信道
void createSubProcess(std::vector<subEndPoint> *subs, std::vector<func_t> &FuncMap)
{
    std::vector<int> deleteFd; // 保存写端fd
    for (int i = 0; i < PROCSS_NUM; i++)
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;

        // 每次创建新的子进程，都会继承上一个进程的fd
        // 导致某个子进程写端没有被完全关掉----已解决
        pid_t id = fork();
        if (id == 0)
        {
            //子进程关闭上一个进程的写端
            for (int i = 0; i < deleteFd.size(); ++i)
                close(deleteFd[i]);

            // 子进程, 进行处理任务
            close(fds[1]);
            while (true)
            {
                // 1.获取命令码，如果没有发送，子进程阻塞
                int commandCode = recvTask(fds[0]);
                //std::cout<<"子进程fds[0] "<<fds[0]<<std::endl;
                // 2.完成任务
                if (commandCode >= 0 && commandCode < FuncMap.size())
                //回调函数
                    FuncMap[commandCode]();
                else if (commandCode == -1)
                    // std::cout<<"sub recvTask error"<<std::endl;
                    break;
            }

            exit(0);
        }
        // 父进程
        close(fds[0]);
        subEndPoint sub(id, fds[1]); // 构建对象
        //std::cout<<"父进程：fds[1]"<<fds[1]<<std::endl;
        subs->push_back(sub);        // 添加到进程信息表

        deleteFd.push_back(fds[1]);
    }
}
/***********************负载均衡分配任务****************************/
void loadBlanceContrl(const std::vector<subEndPoint> &subs, const std::vector<func_t> &FuncMap, int cnt)
{
    int processnum = subs.size();
    int tasknum = FuncMap.size();
    bool quit = (cnt == 0 ? true : false);
    while (true)
    {
        // 1.选择一个子进程--》在vector<subEedpointer>随机选择一个进程
        int subindex = rand() % processnum;

        // 2.选择一个任务-》在vector<FuncMap>选择一个任务
        int taskindex = rand() % tasknum;

        // 3.任务发送给选择的进程
        sendTask(subs[subindex], taskindex); // 父进程只需要把任务编号发给子进程，子进程就可以拿到任务

        sleep(1);
        if (!quit)
        {
            cnt--;
            if (cnt == 0)
                break;
        }
    }
    // 通过关闭写端，关闭子进程读端，bug下运行看似顺序关闭，实则倒序
    // for (int i = 0; i < processnum; ++i)
    //     close(subs[i]._writeFd);
}
/***************回收子进程********************/
void waitprocess(std::vector<subEndPoint> subs)
{
    int processnum = subs.size();
    for (int i = 0; i < processnum; ++i)
    {
        waitpid(subs[i]._subId, nullptr, 0);
        std::cout << "Wait sub process success..." << std::endl;
    }
}
int main()
{
    MAKE_RAND(); // 生成随机数种子
    // 1. 建立子进程并建立和子进程通信的信道

    // 加载方法表
    std::vector<func_t> FuncMap;
    loadTaskFunc(&FuncMap);

    // 创建子进程维护好父子通信信道
    std::vector<subEndPoint> subs;
    createSubProcess(&subs, FuncMap);

    // 2. 走到这里就是父进程, 控制子进程，负载均衡向子进程发送命令码
    int taskCnt = 0; //>0运行20次，如果是0一直运行
    loadBlanceContrl(subs, FuncMap, taskCnt);

    // 3. 回收子进程信息
    waitprocess(subs);
}